package com.example.activiti.controller;

import java.util.List;
import org.activiti.engine.identity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.activiti.dto.ComplateActivitiApplyDto;
import com.example.activiti.dto.publicDto.ActivitiRequestDto;
import com.example.activiti.dto.publicDto.ActivitiResponseDto;
import com.example.activiti.service.ComplateActivitiService;

@RestController  
@RequestMapping("/complateActivitiController")
public class ComplateActivitiController {
	@Autowired
	private ComplateActivitiService complateActivitiService;
    /**
     * Service负责执行动作，Query负责执行查询，
	  * 也就是涉及到数据的增、删、改由Service负责，涉及到数据的查由Query负责，
	  * 在spring boot中，Service可以通过注入获取，
	 * Query可以通过相应的Service获取，
	  * 所有的Service都可以通过ProcessEngine获取。
     */
    /**
	 * ProcessEngine包含所有的service
	 * activiti有8个service管理着activiti所有的资源
	 * TaskService-对用户任务进行操作和查询
	 * RepositoryService-对activiti资源进行操作，比如部署文件，附件
	 * RuntimeService-运行时服务，可以对运行时流程进行修改，如增加变量，移除变量等
	 * IdentityService-身份认证服务，对用户，用户组，用户角色进行操作
	 * HistoryService-历史记录服务，对审批历史进行操作
	 * FormService-表单服务，操作表单数据
	 * DynamicBpmnService-通过该服务，可以动态修改流程
	 * ManagementService-管理服务，查看当前activiti系统信息，不会在应用里用到，一般用于管理系统里
	 */
	
	/**
	 * 在activiti中有几个概念经常用，但文档也没有讲的很清楚，
	 * 如果不理解，有可能会误用。本文就详细探讨以下几个概念
	 * deployment（部署）
	 * instance（实例）
	 * execution（执行）
	 * task（任务）
	 * 
	 * 
	 * deployment（部署）:
	 * bpmn流程模型文件需要通过RepositoryService进行发布，
	  * 相同名称的流程模型重新部署后会升一个版本，但并不影响之前的流程，
	  * 还未结束的流程还是会按照发起时候的版本走。
	  * 因此deployment就表示一次资源的部署，
	  * 资源类型不一定是bpmn文件，可以是任意文件。
	  * 上传的资源元信息保存在ACT_RE_DEPLOYMENT中，
	  * 资源内容以二进制形式保存在表ACT_GE_BYTEARRAY中，
	  * 两张表通过DEPLOYMENT_ID字段关联
	 * 
	 * 
	 * instance（流程实例）:
	  * 一个流程每发起一次就是一个实例，实例数也是流程统计的重要数据，
	  * 一个流程如果有10000个实例就表示这个流程已经被发起了10000次，
	  * 流程实例在activiti中是没有实体表存储的，在流程发起时系统分配一个实例id(PROC_INST_ID_)，
	  * 在整个流程的流转中，该实例号都保持不变，
	  * 因此可以根据PROC_INST_ID_查询整个流程运行时的数据。
	  * 当调用api发起流程时，应用程序应需要把PROC_INST_ID_保存到自身的业务表中，
	  * 后续对流程的管理和追踪都需要PROC_INST_ID_，如要查询指定流程目前审批人信息，
	  * 执行以下api：TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService();  
					List<Task> tasks = taskService.createTaskQuery()  
        			.processInstanceId(instanceId)  
        			.list();
     *
     *
     *  execution（执行）:
     *  execution是比较难理解的一个概念，代表流程实例的执行路径，有点抽象，
          *  什么意思呢，当流程没有分支，start到end就是一条直线，那么就只有一条执行路径，就是一个execution，
          *  如果流程有分支，比如平行审批，那么到平行网关就会分出两条路径，这时候就会产生两个的execution，
          *  新的execution通过PARENT_ID_与父exection进行关联，
          *  之所以要这么设计，我觉得是为了隔离不同执行路径间数据的隔离，
          *  比如在程序分支A上定义了一个变量，程序是不知道流程是否在分支上，
          *  在分支B上又重新定义了相同的变量，如果没有execution的概念就会导致变量被覆盖。
     *  
     *  
     *  task（任务）：
          *  这里说的task指的是usertask，usertask就是一个人的待办，
          *  可以通过activiti提供的api可以查询指定人当前待办信息，当activiti遇到usertask节点就会进入等待状态，
          *  等待用户审批完成后继续执行。用户的待办任务存储在ACT_RU_TASK表上。
     *
     *  
     *  
     *  ProcessDefinition (流程定义)：
     *  流程定义ProcessDefinition是对业务过程的完整描述，例如请假流程定义、报销流程定义等。
     *  流程定义的管理包括部署流程定义、查询流程定义、查看流程定义图和删除流程定义。
	 */
    /**
          * 对外发布的接口：
     * 1、发起流程接口，用于发起请假申请
     * 2、根据分组获取用户的列表，用来查询用户使前端可以选择需要审批的人
     * 3、审核人的审核通过接口，用于审核申请
     * 4、审核人的审核不通过接口，用于审核不通过时调用
     * 5、打回上一节点的接口，用于发现上一部门审核出问题时流程打回
     * 6、查询历史记录的接口，用来查询已审批或者已打回和已拒绝的流程
     * 7、获取当前处理人
     */
    
    
    /**
     * complateQingjia
          * 发布流程（发起流程）
     * @param processesName 流程名称，也就是流程id
     * @param startUser 发起人账户
     * 以上是必传字段，以下是非必传字段
     * @param reqeustBody 传的一些申请相关的内容
     * @param reason 申请理由
     * @param day 申请填数
     * @param biginDay 其实日期yyyy-MM-dd格式
     * @param account 通startUser发起人账户
     */
    @PostMapping("runtimeRelease")
    public List<ActivitiResponseDto> runtimeRelease(@RequestBody ActivitiRequestDto<ComplateActivitiApplyDto> activitiRequestDto){
        return complateActivitiService.runtimeRelease(activitiRequestDto);
    }
    
    /**
     * 获取下一节点的所有审批人
     * @param instanceId 流程实例的id
     */
    @PostMapping("getNextUsers")
    public List<User> getNextUsers(@RequestParam("instanceId")String instanceId){
    	return complateActivitiService.getNextUsers(instanceId);
    }

    
    /**
     * 根据分组名称和processInstanceId获取Task，是用来获取taskid的，因为需要新的taskid来分配到人
     * @param groupName 分组名称
     * @param instanceId 流程实例的id
     * @return
     */
    @PostMapping("getTaskByGroupInstance")
    public List<ActivitiResponseDto> getTaskByGroupInstance(@RequestParam("groupName")String groupName, 
    		@RequestParam("instanceId")String instanceId){
        return complateActivitiService.getTaskByGroupInstance(groupName, instanceId);
    }
    
    /**
     * 获取所有的审批人之后选择一个审批人，
     *并为该审批人拾取任务，这样才能提交任务
     *@param taskId 当前审批过后非配后的任务id，可从分组任务中获取
     *@param assignee 页面传递过来的用户的账户
     */
    @PostMapping("claimTask")
    public void claimTask(@RequestParam("taskId")String taskId, 
    		@RequestParam("assignee")String assignee){
        complateActivitiService.claimTask(taskId, assignee);
    }
    
    /**
     * 根据用户的账户查询该账户下所有的待办信息和审核意见
     * @param assignee 用户账户
     * @param lastTaskId 上一个任务的id
     * @return
     */
    @GetMapping("getTaskByUser")
    public List<ActivitiResponseDto> getTaskByUser(@RequestParam("assignee")String assignee){
        return complateActivitiService.getTaskByUser(assignee);
    }
    
    /**
     * 完成待办任务
     * @param taskId 任务id
     * @param opinion 审批意见
     */
    @PostMapping("taskComplateByTaskId")
    public void taskComplateByTaskId(@RequestParam("taskId")String taskId, 
    		@RequestParam("opinion")String opinion) {
    	complateActivitiService.taskComplateByTaskId(taskId, opinion);
    }
}
